// client.js 使用 ES 模块语法
import _axios from 'axios'
import event from '../eventCenter/index'
import { getAddParams } from './utils.js'
const baseUrl = import.meta.env.VITE_APP_BASE_URL
// 请求等待队列
const debounceRequests = new Map()
const throttleRequests = new Set()
const debounceTothrottleMap = new Map()
const waitRequests = new Map()
const defaultConfig = {
  baseURL: baseUrl + '/api' || '',
  crossDomain: true, // 允许跨域
  debounceWaitTime: 500, // 防抖等待时间(毫秒)
  throttleWaitTime: 500, // 节流等待时间(毫秒)
  // withCredentials: true, // 跨域携带cookie
  timeout: 120000,
  validateStatus(status) {
    return status === 200
  }
}
/**
 * 生成请求唯一标识
 * @param {Object} config 请求配置
 * @returns {string} 唯一标识
 */
function getDebounceRequestsKey(config) {
  return [
    config.method?.toUpperCase() || 'GET', // 请求方法
    config.url // 请求路径
  ].join('&')
}
function getThrottleRequestsKey(config) {
  return [
    config.method?.toUpperCase() || 'GET', // 请求方法
    config.url, // 请求路径
    JSON.stringify(config.params), // 请求参数
    JSON.stringify(config.data) // 请求体数据
  ].join('&')
}

/**
 * 创建防抖axios实例
 * @param {Object} defaultConfig 默认配置
 * @param {number} waitTime 防抖等待时间(毫秒)
 * @returns {AxiosInstance} 封装后的实例
 */
const axios = _axios.create(defaultConfig)

// 请求拦截器
axios.interceptors.request.use(config => {
  const throttlekey = getThrottleRequestsKey(config)
  const debouncekey = getDebounceRequestsKey(config)
  // 如果需要保证请求响应结果是最新的
  // 方案一，设置请求队列，
  if (config.isNeedNew === true) {
    console.log('waitRequests', waitRequests)
    if (waitRequests.has(debouncekey)) {
      const { cancel } = waitRequests.get(debouncekey)
      cancel(`取消: ${debouncekey}`)
      waitRequests.delete(debouncekey)
    }
  }

  if (config.throttle !== false) {
    // 节流检查：如果在节流时间内已经发送了相同的请求，则跳过
    if (throttleRequests.has(throttlekey)) {
      console.log('节流:', throttlekey)
      return Promise.reject(new _axios.Cancel(`节流: ${debouncekey},${throttlekey}`))
    }
  }

  if (config.debounce !== false) {
    // 防抖处理：取消前一个相同请求
    if (debounceRequests.has(debouncekey)) {
      const { cancel } = debounceRequests.get(debouncekey)
      cancel(`防抖: ${debouncekey}`)
    }
  }
  throttleRequests.add(throttlekey)

  setTimeout(() => {
    throttleRequests.delete(throttlekey)
  }, config.throttleWaitTime || defaultConfig.throttleWaitTime)
  // 创建新的取消令牌
  const source = _axios.CancelToken.source()
  config.cancelToken = source.token

  return new Promise((resolve, reject) => {
    const timer = setTimeout(() => {
      debounceRequests.delete(debouncekey)
      debounceTothrottleMap.delete(debouncekey)
      resolve(config)
    }, config.debounceWaitTime || defaultConfig.debounceWaitTime)
    // 创建可传播的取消方法
    const cancel = message => {
      clearTimeout(timer)
      if (debounceTothrottleMap.has(debouncekey)) {
        //防抖转节流
        const oldThrottlekey = debounceTothrottleMap.get(debouncekey)
        throttleRequests.delete(oldThrottlekey)
      }
      const error = new _axios.Cancel(message)
      source.cancel(message) // 触发 axios 的取消机制
      reject(error) // 关闭本次请求
    }
    debounceRequests.set(debouncekey, { timer, cancel })
    debounceTothrottleMap.set(debouncekey, throttlekey)
    // 如果需要保证请求响应结果是最新的
    // 方案一，设置请求队列，
    waitRequests.set(debouncekey, { timer, cancel })
  })
})

// 响应拦截器
axios.interceptors.response.use(
  response => {
    // 如果需要保证请求响应结果是最新的
    // 方案一，设置请求队列，
    const waitKey = getDebounceRequestsKey(response.config)
    if (waitRequests.has(waitKey)) {
      waitRequests.delete(waitKey)
    }
    if (response.data.code === 401) {
      event.emit('RELOGIN')
    }
    return response
  },
  error => {
    // 区分取消错误类型：节流/防抖产生的取消不抛错
    if (_axios.isCancel(error)) {
      // 检查错误信息是否包含节流/防抖标识
      if (error.message.includes('节流:') || error.message.includes('防抖:')) {
        // 返回一个空的成功Promise，让业务代码认为请求已完成（但无数据）
        return Promise.resolve({ data: { code: 0, message: '请求已节流/防抖处理' } })
      }
    }
    return Promise.reject(error)
  }
)
function request(opt) {
  let resPro = new Promise((resolve, reject) => {
    // 处理加载状态
    if (opt.load) {
      if (opt._type === 'blob') {
        event.emit('blobload')
      } else {
        event.emit('load')
      }
    }

    // 处理文件下载进度
    const config = {
      ...opt,
      // 只有下载文件时才监听进度
      onDownloadProgress:
        opt._type === 'blob'
          ? progressEvent => {
              console.log('progressEvent', progressEvent)
              if (progressEvent.total) {
                // 计算下载百分比
                const percent = Math.round((progressEvent.loaded / progressEvent.total) * 100)
                // 触发进度事件，传递百分比
                event.emit('downloadProgress', {
                  percent,
                  loaded: progressEvent.loaded,
                  total: progressEvent.total,
                  url: opt.url
                })
              }
            }
          : undefined
    }

    axios
      .request(config)
      .then(res => {
        if (opt._type === 'blob') {
          resolve(res.data)
        } else {
          if (typeof res.data === 'object' && res.data.data === undefined) {
            res.data.data = {}
          }
          resolve(res.data)
        }
      })
      .catch(error => {
        reject(error)
      })
  })
  return resPro
}

function customAxios(opt) {
  return request(opt)
}

customAxios.defaults = axios.defaults
customAxios.request = request

// GET请求
customAxios.get = function (url, params = {}, opt = {}) {
  return request(Object.assign({ method: 'get', url, params }, opt, getAddParams()))
}

// POST请求
customAxios.post = function (url, data = {}, opt = {}) {
  return request(Object.assign({ method: 'post', url, data }, opt, getAddParams()))
}

// PUT请求
customAxios.put = function (url, data = {}, opt = {}) {
  return request(Object.assign({ method: 'put', url, data }, opt, getAddParams()))
}

// DELETE请求
customAxios.delete = function (url, data = {}, opt = {}) {
  return request(Object.assign({ method: 'delete', url, data }, opt, getAddParams()))
}

//  blob下载（带进度）
customAxios.blob = function (url, params = {}, opt = {}) {
  return request(
    Object.assign(
      {
        method: 'get',
        url,
        params,
        _type: 'blob',
        load: true,
        // 必须设置响应类型为blob
        responseType: 'blob'
      },
      opt,
      getAddParams()
    )
  )
}

export default customAxios
